/*******************************************************************************
** Qt Advanced Docking System
** Copyright (C) 2017 Uwe Kindler
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/


//============================================================================
/// \file   DockAreaTabBar.cpp
/// \author Uwe Kindler
/// \date   24.08.2018
/// \brief  Implementation of CDockAreaTabBar class
//============================================================================

//============================================================================
//                                   INCLUDES
//============================================================================
#include "FloatingDragPreview.h"
#include "DockAreaTabBar.h"

#include <QMouseEvent>
#include <QScrollBar>
#include <QDebug>
#include <QBoxLayout>
#include <QApplication>
#include <QtGlobal>
#include <QTimer>

#include "FloatingDockContainer.h"
#include "DockAreaWidget.h"
#include "DockOverlay.h"
#include "DockManager.h"
#include "DockWidget.h"
#include "DockWidgetTab.h"

#include <iostream>


namespace ads
{
/**
 * CDockAreaTabBar类的私有数据类（pimpl）
 */
struct DockAreaTabBarPrivate
{
    CDockAreaTabBar* _this;
    CDockAreaWidget* DockArea;
    QWidget* TabsContainerWidget;
    QBoxLayout* TabsLayout;
    int CurrentIndex = -1;

    /**
     * 私有数据构造函数
     */
    DockAreaTabBarPrivate(CDockAreaTabBar* _public);

    /**
     * 在当前索引更改或删除选项卡后更新选项卡。
     * 该函数重新分配样式表以更新选项卡。
     */
    void updateTabs();

    /**
     * 方便访问第一个选项卡的函数
     */
    CDockWidgetTab* firstTab() const { return _this->tab(0); }

    /**
     * 方便访问最后一个选项卡的函数
     */
    CDockWidgetTab* lastTab() const { return _this->tab(_this->count() - 1); }
};

// struct DockAreaTabBarPrivate
//============================================================================
DockAreaTabBarPrivate::DockAreaTabBarPrivate(CDockAreaTabBar* _public) :
    _this(_public)
{
}

//============================================================================
void DockAreaTabBarPrivate::updateTabs()
{
    // 设置活动选项卡并更新其他选项卡为非活动状态
    for (int i = 0; i < _this->count(); ++i)
    {
        auto TabWidget = _this->tab(i);
        if (!TabWidget)
        {
            continue;
        }
        if (i == CurrentIndex)
        {
            TabWidget->show();
            TabWidget->setActiveTab(true);
            // 有时同步计算矩形区域失败
            // 因此我们在这里使用QTimer::singleShot在事件循环中执行调用 - 参见＃520
            QTimer::singleShot(0, [&, TabWidget] {
                _this->ensureWidgetVisible(TabWidget);
            });
        }
        else
        {
            TabWidget->setActiveTab(false);
        }
    }
}

//============================================================================
CDockAreaTabBar::CDockAreaTabBar(CDockAreaWidget* parent) :
    QScrollArea(parent),
    d(new DockAreaTabBarPrivate(this))
{
    d->DockArea = parent;
    setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
    setFrameStyle(QFrame::NoFrame);
    setWidgetResizable(true);
    setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    d->TabsContainerWidget = new QWidget();
    d->TabsContainerWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
    d->TabsContainerWidget->setObjectName("tabsContainerWidget");
    d->TabsLayout = new QBoxLayout(QBoxLayout::LeftToRight);
    d->TabsLayout->setContentsMargins(0, 0, 0, 0);
    d->TabsLayout->setSpacing(0);
    d->TabsLayout->addStretch(1);
    d->TabsContainerWidget->setLayout(d->TabsLayout);
    setWidget(d->TabsContainerWidget);
    setFocusPolicy(Qt::NoFocus);
}

//============================================================================
CDockAreaTabBar::~CDockAreaTabBar()
{
    delete d;
}

//============================================================================
void CDockAreaTabBar::wheelEvent(QWheelEvent* Event)
{
    Event->accept();
    const int direction = Event->angleDelta().y();
    if (direction < 0)
    {
        horizontalScrollBar()->setValue(horizontalScrollBar()->value() + 20);
    }
    else
    {
        horizontalScrollBar()->setValue(horizontalScrollBar()->value() - 20);
    }
}

//============================================================================
void CDockAreaTabBar::setCurrentIndex(int index)
{
    if (index == d->CurrentIndex)
    {
        return;
    }
    if (index < -1 || index > (count() - 1))
    {
        qWarning() << Q_FUNC_INFO << "无效的索引" << index;
        return;
    }
    Q_EMIT currentChanging(index);
    d->CurrentIndex = index;
    d->updateTabs();
    updateGeometry();
    Q_EMIT currentChanged(index);
}

//============================================================================
int CDockAreaTabBar::count() const
{
    // 选项卡栏包含一个伸缩项作为最后一项
    return d->TabsLayout->count() - 1;
}

//===========================================================================
void CDockAreaTabBar::insertTab(int Index, CDockWidgetTab* Tab)
{
    d->TabsLayout->insertWidget(Index, Tab);
    connect(Tab, SIGNAL(clicked()), this, SLOT(onTabClicked()));
    connect(Tab, SIGNAL(closeRequested()), this, SLOT(onTabCloseRequested()));
    connect(Tab, SIGNAL(closeOtherTabsRequested()), this, SLOT(onCloseOtherTabsRequested()));
    connect(Tab, SIGNAL(moved(QPoint)), this, SLOT(onTabWidgetMoved(QPoint)));
    connect(Tab, SIGNAL(elidedChanged(bool)), this, SIGNAL(elidedChanged(bool)));
    Tab->installEventFilter(this);
    Q_EMIT tabInserted(Index);
    if (Index <= d->CurrentIndex)
    {
        setCurrentIndex(d->CurrentIndex + 1);
    }
    else if (d->CurrentIndex == -1)
    {
        setCurrentIndex(Index);
    }
    updateGeometry();
}


//===========================================================================
// 移除指定的Tab
void CDockAreaTabBar::removeTab(CDockWidgetTab* Tab)
{
    if (!count())
    {
        return;
    }
    ADS_PRINT("CDockAreaTabBar::removeTab ");

    int NewCurrentIndex = currentIndex();
    int RemoveIndex = d->TabsLayout->indexOf(Tab);

    if (count() == 1)
    {
        NewCurrentIndex = -1;
    }

    if (NewCurrentIndex > RemoveIndex)
    {
        NewCurrentIndex--;
    }
    else if (NewCurrentIndex == RemoveIndex)
    {
        NewCurrentIndex = -1;

        // 从当前Tab向右查找下一个可见的Tab
        for (int i = (RemoveIndex + 1); i < count(); ++i)
        {
            if (tab(i)->isVisibleTo(this))
            {
                NewCurrentIndex = i - 1;
                break;
            }
        }

        // 如果右侧没有可见的Tab，则从当前Tab向左查找下一个可见的Tab
        if (NewCurrentIndex < 0)
        {
            for (int i = (RemoveIndex - 1); i >= 0; --i)
            {
                if (tab(i)->isVisibleTo(this))
                {
                    NewCurrentIndex = i;
                    break;
                }
            }
        }
    }

    Q_EMIT removingTab(RemoveIndex);
    d->TabsLayout->removeWidget(Tab);
    Tab->disconnect(this);
    Tab->removeEventFilter(this);

    ADS_PRINT("NewCurrentIndex " << NewCurrentIndex);

    if (NewCurrentIndex != d->CurrentIndex)
    {
        setCurrentIndex(NewCurrentIndex);
    }
    else
    {
        d->updateTabs();
    }

    updateGeometry();
}
//===========================================================================
// 获取当前Tab的索引
int CDockAreaTabBar::currentIndex() const
{
    return d->CurrentIndex;
}
//===========================================================================
// 获取当前Tab
CDockWidgetTab* CDockAreaTabBar::currentTab() const
{
    if (d->CurrentIndex < 0)
    {
        return nullptr;
    }
    else
    {
        return qobject_cast<CDockWidgetTab*>(d->TabsLayout->itemAt(d->CurrentIndex)->widget());
    }
}
//===========================================================================
// Tab被点击时的槽函数
void CDockAreaTabBar::onTabClicked()
{
    CDockWidgetTab* Tab = qobject_cast<CDockWidgetTab*>(sender());
    if (!Tab)
    {
        return;
    }

    int index = d->TabsLayout->indexOf(Tab);
    if (index < 0)
    {
        return;
    }

    setCurrentIndex(index);
    Q_EMIT tabBarClicked(index);
}
//===========================================================================
// Tab关闭请求时的槽函数
void CDockAreaTabBar::onTabCloseRequested()
{
    CDockWidgetTab* Tab = qobject_cast<CDockWidgetTab*>(sender());
    int Index = d->TabsLayout->indexOf(Tab);
    closeTab(Index);
}
//===========================================================================
// 关闭其他Tab的请求时的槽函数
void CDockAreaTabBar::onCloseOtherTabsRequested()
{
    auto Sender = qobject_cast<CDockWidgetTab*>(sender());

    for (int i = 0; i < count(); ++i)
    {
        auto Tab = tab(i);

        if (Tab->isClosable() && !Tab->isHidden() && Tab != Sender)
        {
            int Offset = Tab->dockWidget()->features().testFlag(CDockWidget::DockWidgetDeleteOnClose) ? 1 : 0;
            closeTab(i);

            if (Tab->dockWidget()->isClosed())
            {
                i -= Offset;
            }
        }
    }
}
//===========================================================================
// 获取指定索引的Tab
CDockWidgetTab* CDockAreaTabBar::tab(int Index) const
{
    if (Index >= count() || Index < 0)
    {
        return nullptr;
    }

    return qobject_cast<CDockWidgetTab*>(d->TabsLayout->itemAt(Index)->widget());
}
//===========================================================================
// Tab被拖动时的槽函数
void CDockAreaTabBar::onTabWidgetMoved(const QPoint& GlobalPos)
{
    CDockWidgetTab* MovingTab = qobject_cast<CDockWidgetTab*>(sender());
    if (!MovingTab)
    {
        return;
    }

    int fromIndex = d->TabsLayout->indexOf(MovingTab);
    auto MousePos = mapFromGlobal(GlobalPos);
    MousePos.rx() = qMax(d->firstTab()->geometry().left(), MousePos.x());
    MousePos.rx() = qMin(d->lastTab()->geometry().right(), MousePos.x());

    int toIndex = -1;

    // 查找鼠标下方的Tab
    for (int i = 0; i < count(); ++i)
    {
        CDockWidgetTab* DropTab = tab(i);

        if (DropTab == MovingTab || !DropTab->isVisibleTo(this) || !DropTab->geometry().contains(MousePos))
        {
            continue;
        }

        toIndex = d->TabsLayout->indexOf(DropTab);

        if (toIndex == fromIndex)
        {
            toIndex = -1;
        }

        break;
    }

    if (toIndex > -1)
    {
        d->TabsLayout->removeWidget(MovingTab);
        d->TabsLayout->insertWidget(toIndex, MovingTab);

        ADS_PRINT("tabMoved from " << fromIndex << " to " << toIndex);
        Q_EMIT tabMoved(fromIndex, toIndex);
        setCurrentIndex(toIndex);
    }
    else
    {
        d->TabsLayout->update();
    }
}
//===========================================================================
// 关闭指定索引的Tab
void CDockAreaTabBar::closeTab(int Index)
{
    if (Index < 0 || Index >= count())
    {
        return;
    }

    auto Tab = tab(Index);

    if (Tab->isHidden())
    {
        return;
    }

    Q_EMIT tabCloseRequested(Index);
}
//===========================================================================
// 事件过滤器
bool CDockAreaTabBar::eventFilter(QObject *watched, QEvent *event)
{
    bool Result = Super::eventFilter(watched, event);
    CDockWidgetTab* Tab = qobject_cast<CDockWidgetTab*>(watched);

    if (!Tab)
    {
        return Result;
    }

    switch (event->type())
    {
    case QEvent::Hide:
        Q_EMIT tabClosed(d->TabsLayout->indexOf(Tab));
        updateGeometry();
        break;
    case QEvent::Show:
        Q_EMIT tabOpened(d->TabsLayout->indexOf(Tab));
        updateGeometry();
        break;
    case QEvent::LayoutRequest:
        updateGeometry();
        break;
    default:
        break;
    }

    return Result;
}


// 检查指定的索引是否为打开的标签
bool CDockAreaTabBar::isTabOpen(int Index) const
{
    // 如果索引小于0或大于等于标签数量，则返回false
    if (Index < 0 || Index >= count())
    {
        return false;
    }
    // 如果标签隐藏，则返回false；否则返回true
    return !tab(Index)->isHidden();
}

// 返回最小尺寸
QSize CDockAreaTabBar::minimumSizeHint() const
{
    // 获取推荐尺寸
    QSize Size = sizeHint();
    // 设置宽度为10
    Size.setWidth(10);
    return Size;
}

// 返回尺寸
QSize CDockAreaTabBar::sizeHint() const
{
    // 返回标签容器小部件的推荐尺寸
    return d->TabsContainerWidget->sizeHint();
}

// 获取指定位置的标签索引
int CDockAreaTabBar::tabAt(const QPoint& Pos) const
{
    // 如果标签栏不可见，则返回无效索引
    if (!isVisible())
    {
        return TabInvalidIndex;
    }
    // 如果位置的x坐标小于第一个标签的x坐标，则返回-1
    if (Pos.x() < tab(0)->geometry().x())
    {
        return -1;
    }
    // 遍历所有标签
    for (int i = 0; i < count(); ++i)
    {
        // 如果位置在标签的几何范围内，则返回该标签的索引
        if (tab(i)->geometry().contains(Pos))
        {
            return i;
        }
    }
    // 如果位置不在任何标签的范围内，则返回标签数量
    return count();
}

// 获取指定位置的标签插入索引
int CDockAreaTabBar::tabInsertIndexAt(const QPoint& Pos) const
{
    // 获取位置处的标签索引
    int Index = tabAt(Pos);
    // 如果索引无效，则返回默认插入索引
    if (Index == TabInvalidIndex)
    {
        return TabDefaultInsertIndex;
    }
    else
    {
        // 如果索引小于0，则返回0；否则返回索引值
        return (Index < 0) ? 0 : Index;
    }
}

} // namespace ads


//---------------------------------------------------------------------------
// EOF DockAreaTabBar.cpp
